import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:mockito/mockito.dart' as mo;

import '../DioExtens.dart';
import '../common/test_page.dart';
import '../mock/adapters.dart';
import '../mock/http_mock.dart';
import '../mock/http_mock.mocks.dart';

class StacktraceTestPage extends TestPage {
  StacktraceTestPage(super.title) {
    group('Dio异常堆栈跟踪', () {
      test('${DioExceptionType.badResponse}', () async {
        final dio = getDio()
          ..httpClientAdapter = MockAdapter()
          ..options.baseUrl = MockAdapter.mockBase;

        expect(
          dio.get('/foo'),
          '',
        );
      });

      test('${DioExceptionType.cancel}', () async {
        final dio = getDio()
          ..httpClientAdapter = MockAdapter()
          ..options.baseUrl = MockAdapter.mockBase;

        final token = CancelToken();
        Future.delayed(const Duration(milliseconds: 10), () {
          token.cancel('cancelled');
          dio.httpClientAdapter.close(force: true);
        });

        expect(
          dio.get('/test-timeout', cancelToken: token),
          '',
        );
      });

      test(
        '${DioExceptionType.connectionTimeout}',
            () async {
          await HttpOverrides.runWithHttpOverrides(() async {
            final timeout = Duration(milliseconds: 10);
            final dio = getDio()
              ..options.connectTimeout = timeout
              ..options.baseUrl = 'https://does.not.exist';

            expect(
              dio.get('/test'),
              '',
            );
          }, MockHttpOverrides());
        }
      );

      test(
        '${DioExceptionType.receiveTimeout}',
            () async {
          await HttpOverrides.runWithHttpOverrides(() async {
            final timeout = Duration(milliseconds: 10);
            final dio = getDio()
              ..options.receiveTimeout = timeout
              ..options.baseUrl = 'https://does.not.exist';

            expect(
              dio.get('/test'),
              '',
            );
          }, MockHttpOverrides());
        }
      );

      test(
        '${DioExceptionType.sendTimeout}',
            () async {
          await HttpOverrides.runWithHttpOverrides(() async {
            final timeout = Duration(milliseconds: 10);
            final dio = getDio()
              ..options.sendTimeout = timeout
              ..options.baseUrl = 'https://does.not.exist';

            expect(
              dio.get('/test', data: 'some data'),
              '',
            );
          }, MockHttpOverrides());
        }
      );

      test(
        '${DioExceptionType.badCertificate}',
            () async {
          await HttpOverrides.runWithHttpOverrides(() async {
            final dio = getDio()
              ..options.baseUrl = 'https://does.not.exist'
              ..httpClientAdapter = IOHttpClientAdapter(
                validateCertificate: (_, __, ___) => false,
              );

            expect(
              dio.get('/test'),
              '',
            );
          }, MockHttpOverrides());
        }
      );
      group('Dio异常类型.连接错误', () {
        test(
          '请求时的套接字异常',
              () async {
            final dio = getDio()
              ..options.baseUrl = 'https://does.not.exist'
              ..httpClientAdapter = IOHttpClientAdapter();
            expect(
              dio.get('/test', data: 'test'),
              '',
            );
          }
        );
      });
      group('Dio异常类型.未知的', () {
        test(
          '$JsonUnsupportedObjectError',
              () async {
            final dio = getDio()..options.baseUrl = 'https://does.not.exist';

            expect(
              dio.get(
                '/test',
                options: Options(contentType: Headers.jsonContentType),
                data: Object(),
              ),
              '',
            );
          }
        );

        test(
          '响应时出现套接字异常',
              () async {
            final dio = getDio()
              ..options.baseUrl = 'https://does.not.exist'
              ..httpClientAdapter = IOHttpClientAdapter(
                createHttpClient: () {
                  final request = MockHttpClientRequest();
                  final client = MockHttpClient();
                  mo.when(client.openUrl(mo.any??'', mo.any??Uri.parse('https://www.baidu.com/'))).thenAnswer((_) async => request);
                  mo.when(request.headers).thenReturn(MockHttpHeaders());
                  mo.when(request.addStream((mo.any??'') as Stream<List<int>>)).thenAnswer((_) => Future.value());
                  mo.when(request.close()).thenAnswer(
                        (_) => Future.delayed(Duration(milliseconds: 50), () {
                      throw SocketException('test');
                    }),
                  );
                  return client;
                },
              );

            expect(
              dio.get('/test', data: 'test'),
              '',
            );
          }
        );
      });

      test('拦截器在出现错误时获取堆栈跟踪', () async {
        final dio = getDio()
          ..options.baseUrl = EchoAdapter.mockBase
          ..httpClientAdapter = EchoAdapter();

        StackTrace caughtStackTrace;
        dio.interceptors.addAll([
          InterceptorsWrapper(
            onError: (err, handler) {
              caughtStackTrace = err.stackTrace;
              handler.next(err);
            },
          ),
          InterceptorsWrapper(
            onRequest: (options, handler) {
              final error = DioException(error: Error(), requestOptions: options);
              handler.reject(error, true);
            },
          ),
        ]);

        expect(
          dio.get('/error'),
          ''
        );
      });

      test('队列侦听器在出现错误时获取堆栈跟踪', () async {
        final dio = getDio()
          ..options.baseUrl = EchoAdapter.mockBase
          ..httpClientAdapter = EchoAdapter();

        StackTrace caughtStackTrace;
        dio.interceptors.addAll([
          QueuedInterceptorsWrapper(
            onError: (err, handler) {
              caughtStackTrace = err.stackTrace;
              handler.next(err);
            },
          ),
          QueuedInterceptorsWrapper(
            onRequest: (options, handler) {
              final error = DioException(
                error: Error(),
                requestOptions: options,
              );
              handler.reject(error, true);
            },
          ),
        ]);

        expect(
          dio.get('/error'),
          '',
        );
      });
    });
  }

}